#include "key.h"

#define KEY_LONG_PRESS_TIME 100

#define KEY_PRESS(_key) (_key.pin_state == GPIO_PIN_RESET)

typedef enum
{
    KEY_STATE_IDLE = 0,
    KEY_STATE_DOWN_BOUNCING = 1,
    KEY_STATE_DOWN = 2,
    KEY_STATE_UP_BOUNCING = 3,
    KEY_STATE_LONG_DOWN = 4,
    KEY_STATE_LONG_UP_BOUNCING = 5,
} KEY_State;

typedef struct
{
    GPIO_PinState pin_state;
    KEY_State state;
    uint32_t tick;
} KEY_Handler;

extern KEY_Handler key[4];

KEY_Handler key[4];

__weak void key_press_callback(uint8_t index)
{
    UNUSED(index);
}

__weak void key_release_callback(uint8_t index)
{
    UNUSED(index);
}

__weak void key_long_press_callback(uint8_t index)
{
    UNUSED(index);
}

__weak void key_long_release_callback(uint8_t index)
{
    UNUSED(index);
}

void key_state(uint8_t index)
{
    switch (key[index].state)
    {
    case KEY_STATE_IDLE: // Idle
        if (KEY_PRESS(key[index]))
        {
            key[index].state = KEY_STATE_DOWN_BOUNCING;
        }
        break;
    case KEY_STATE_DOWN_BOUNCING: // Down Bouncing
        if (KEY_PRESS(key[index]))
        {
            // --- Key Press ---
            key_press_callback(index);
            // --- Key Press ---
            key[index].state = KEY_STATE_DOWN;
            key[index].tick = 0;
        }
        else
        {
            key[index].state = KEY_STATE_IDLE;
        }
        break;
    case KEY_STATE_DOWN: // Down
        if (KEY_PRESS(key[index]))
        {
            if (key[index].tick++ >= KEY_LONG_PRESS_TIME)
            {
                // --- Long Push ---
                key_long_press_callback(index);
                // --- Long Push ---
                key[index].state = KEY_STATE_LONG_DOWN;
                key[index].tick = 0;
            }
        }
        else
        {
            key[index].state = KEY_STATE_UP_BOUNCING;
            key[index].tick = 0;
        }
        break;
    case KEY_STATE_UP_BOUNCING: // Up Bouncing
        if (KEY_PRESS(key[index]))
        {
            key[index].state = KEY_STATE_DOWN;
        }
        else
        {
            // --- Key Release ---
            key_release_callback(index);
            // --- Key Release ---
            key[index].state = KEY_STATE_IDLE;
        }
        break;
    case KEY_STATE_LONG_DOWN: // Long Down
        if (!KEY_PRESS(key[index]))
        {
            key[index].state = KEY_STATE_LONG_UP_BOUNCING;
        }
        break;
    case KEY_STATE_LONG_UP_BOUNCING: // Long Up Bouncing
        if (KEY_PRESS(key[index]))
        {
            key[index].state = KEY_STATE_LONG_DOWN;
        }
        else
        {
            // --- Long Release ---
            key_long_release_callback(index);
            // --- Long Release ---
            key[index].state = KEY_STATE_IDLE;
        }
        break;
    default: // Error
        key[index].state = KEY_STATE_IDLE;
        key[index].tick = 0;
        break;
    }
}

void key_update(void)
{
    key[0].pin_state = HAL_GPIO_ReadPin(KEY_B1_GPIO_Port, KEY_B1_Pin);
    key[1].pin_state = HAL_GPIO_ReadPin(KEY_B2_GPIO_Port, KEY_B2_Pin);
    key[2].pin_state = HAL_GPIO_ReadPin(KEY_B3_GPIO_Port, KEY_B3_Pin);
    key[3].pin_state = HAL_GPIO_ReadPin(KEY_B4_GPIO_Port, KEY_B4_Pin);

    for (uint8_t i = 0; i < 4; i++)
    {
        key_state(i);
    }
}
